home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 22 / PC Actual CD 22.iso / SHARE / wnt / lemmy30.exe / TCLSCAN.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  26.4 KB  |  992 lines

  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. //  tclscan.c
  4. //
  5. //  Copyright 1997 by James Iuliano & Phil Mercurio
  6. //
  7. //  Permission granted to use this program for development
  8. //  of Lemmy VI editor syntax highlight plug-ins.  Permission
  9. //  for any other use must be obtained by James Iuliano.
  10. //
  11. //    PJM 19970323    First draft, derived from cppscan.c
  12. //    PJM 19970329    Removed preprocessor-line kludge
  13. //    PJM 19970330    Added custom token types
  14. //    PJM 19970330    Added custom keywords (untested)
  15. //    PJM 19970405    Custom keywords working
  16. //    PJM 19970407    First release version
  17. //
  18. //////////////////////////////////////////////////////////////////////////
  19.  
  20. #include <windows.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25.  
  26. #include "syntax.h"
  27.  
  28. /*
  29.  * Define this macro if using theObjects-4.0
  30.  */
  31. //#define USING_TO
  32.  
  33. /*
  34.  * Configure for maximum number of known tokens
  35.  */
  36. #define MAX_KNOWN_TOKENS    (1024)
  37.  
  38. /*
  39.  * Configure for maximum length of a keyword.  Anything
  40.  * longer than this won't even be searched for.
  41.  */
  42. #define MAX_KEYWORD_LEN        (64)
  43.  
  44. /** No configuration below **/
  45.  
  46. /*
  47.  * Configure which Lemmy token ID should be returned for each
  48.  * category of text
  49.  */
  50. #define Unknown     (tk_none)
  51. #define TclKeyword     (tk_cust2)
  52. #define TkKeyword     (tk_cust3)
  53. #define CustomKeyword     (tk_cust4)
  54. #define TOKeyword     (tk_cust5)
  55. #define Comment        (tk_comment)    
  56. #define Braces        (tk_cust0)
  57. #define Brackets    (tk_cust1)
  58. #define Number        (tk_number)
  59.  
  60. /*
  61.  * A list of the custom token types.  The order is
  62.  * important, it has to match the usage of tk_cust?
  63.  * above.
  64.  */
  65. static char* CustomTokenTypes[] = {
  66.     "Tcl Braces {}",
  67.     "Tcl Brackets []",
  68.     "Tcl Keyword",
  69.     "Tk Keyword",
  70.     "Custom Keyword",
  71. #ifdef USING_TO
  72.     "tO Keyword",
  73. #endif
  74. };
  75.  
  76.  
  77. /*
  78.  * The text of a token along with the type it should be 
  79.  * identified as.
  80.  */
  81. typedef struct {
  82.     char*                token;
  83.     parse_token_type    type;
  84. } Token;
  85.  
  86. /*
  87.  * Everything considered "global" to this parser, including
  88.  * the table of known tokens.
  89.  */
  90. typedef struct {
  91.     Token        tokenTable[MAX_KNOWN_TOKENS];
  92.     int            knownTokens;        // size of tokenTable
  93.     int            flag_period;         // Flag for decimal point on decimal numbers
  94.     int            flag_exp;            // Flag for exponent (E|e) on decimal numbers
  95. } TclScanGlobals;
  96.  
  97. /*
  98.  * The initial state of the table of known tokens.
  99.  */
  100. static Token InitTokenTable[] = {
  101. #ifdef USING_TO
  102.     {"args",            TOKeyword},
  103.     {"children",            TOKeyword},
  104.     {"clone",            TOKeyword},
  105.     {"defmethod",            TOKeyword},
  106.     {"defobject",            TOKeyword},
  107.     {"defslot",            TOKeyword},
  108.     {"defsuper",            TOKeyword},
  109.     {"descendants",            TOKeyword},
  110.     {"destruct",            TOKeyword},
  111.     {"exists",            TOKeyword},
  112.     {"hasmethod",            TOKeyword},
  113.     {"hasslot",            TOKeyword},
  114.     {"hastrace",            TOKeyword},
  115.     {"hastraceslot",            TOKeyword},
  116.     {"is ",            TOKeyword},
  117.     {"matches",            TOKeyword},
  118.     {"methods",            TOKeyword},
  119.     {"reclaim",            TOKeyword},
  120.     {"reclaimall",            TOKeyword},
  121.     {"slot",            TOKeyword},
  122.     {"slotappend",            TOKeyword},
  123.     {"slotincr",            TOKeyword},
  124.     {"slotindex",            TOKeyword},
  125.     {"slotinsert",            TOKeyword},
  126.     {"slotlength",            TOKeyword},
  127.     {"slotrange",            TOKeyword},
  128.     {"slotreplace",            TOKeyword},
  129.     {"slots",            TOKeyword},
  130.     {"slotsearch",            TOKeyword},
  131.     {"slotsort",            TOKeyword},
  132.     {"slotunique",            TOKeyword},
  133.     {"slotvar",            TOKeyword},
  134.     {"super",            TOKeyword},
  135.     {"trace",            TOKeyword},
  136.     {"traceslot",            TOKeyword},
  137.     {"unslot",            TOKeyword},
  138.     {"untrace",            TOKeyword},
  139.     {"untraceslot",            TOKeyword},
  140.     {"values",            TOKeyword},
  141.     {"vanilla-object",            TOKeyword},
  142. #endif
  143.     
  144.     {"after",            TclKeyword},
  145.     {"append",            TclKeyword},
  146.     {"array",            TclKeyword},
  147.     {"auto_execok",        TclKeyword},
  148.     {"auto_load",        TclKeyword},
  149.     {"auto_mkindex",    TclKeyword},
  150.     {"auto_reset",        TclKeyword},
  151.     {"bgerror",            TclKeyword},
  152.     {"break",            TclKeyword},
  153.     {"case",            TclKeyword},
  154.     {"catch",            TclKeyword},
  155.     {"cd",                TclKeyword},
  156.     {"clock",            TclKeyword},
  157.     {"close",            TclKeyword},
  158.     {"concat",            TclKeyword},
  159.     {"continue",        TclKeyword},
  160.     {"else",            TclKeyword},
  161.     {"elseif",            TclKeyword},
  162.     {"eof",                TclKeyword},
  163.     {"error",            TclKeyword},
  164.     {"eval",            TclKeyword},
  165.     {"exec",            TclKeyword},
  166.     {"exit",            TclKeyword},
  167.     {"expr",            TclKeyword},
  168.     {"fblocked",        TclKeyword},
  169.     {"fconfigure",        TclKeyword},
  170.     {"file",            TclKeyword},
  171.     {"fileevent",        TclKeyword},
  172.     {"filename",        TclKeyword},
  173.     {"flush",            TclKeyword},
  174.     {"for",                TclKeyword},
  175.     {"foreach",            TclKeyword},
  176.     {"format",            TclKeyword},
  177.     {"gets",            TclKeyword},
  178.     {"glob",            TclKeyword},
  179.     {"global",            TclKeyword},
  180.     {"history",            TclKeyword},
  181.     {"if",                TclKeyword},
  182.     {"incr",            TclKeyword},
  183.     {"info",            TclKeyword},
  184.     {"interp",            TclKeyword},
  185.     {"join",            TclKeyword},
  186.     {"lappend",            TclKeyword},
  187.     {"library",            TclKeyword},
  188.     {"lindex",            TclKeyword},
  189.     {"linsert",            TclKeyword},
  190.     {"list",            TclKeyword},
  191.     {"llength",            TclKeyword},
  192.     {"load",            TclKeyword},
  193.     {"lrange",            TclKeyword},
  194.     {"lreplace",        TclKeyword},
  195.     {"lsearch",            TclKeyword},
  196.     {"lsort",            TclKeyword},
  197.     {"namespace",        TclKeyword},
  198.     {"new",                TclKeyword},
  199.     {"open",            TclKeyword},
  200.     {"otcl_load",        TclKeyword},
  201.     {"otcl_mkindex",    TclKeyword},
  202.     {"package",            TclKeyword},
  203.     {"pid",                TclKeyword},
  204.     {"pkgMkIndex",        TclKeyword},
  205.     {"pkg_mkIndex",        TclKeyword},
  206.     {"proc",            TclKeyword},
  207.     {"puts",            TclKeyword},
  208.     {"pwd",                TclKeyword},
  209.     {"read",            TclKeyword},
  210.     {"regexp",            TclKeyword},
  211.     {"regsub",            TclKeyword},
  212.     {"rename",            TclKeyword},
  213.     {"return",            TclKeyword},
  214.     {"scan",            TclKeyword},
  215.     {"seek",            TclKeyword},
  216.     {"set",                TclKeyword},
  217.     {"socket",            TclKeyword},
  218.     {"source",            TclKeyword},
  219.     {"split",            TclKeyword},
  220.     {"static",            TclKeyword},
  221.     {"string",            TclKeyword},
  222.     {"subst",            TclKeyword},
  223.     {"switch",            TclKeyword},
  224.     {"tcl",                TclKeyword},
  225.     {"tclPkgSetup",        TclKeyword},
  226.     {"tclPkgUnknown",    TclKeyword},
  227.     {"tclvars",            TclKeyword},
  228.     {"tell",            TclKeyword},
  229.     {"time",            TclKeyword},
  230.     {"trace",            TclKeyword},
  231.     {"unknown",            TclKeyword},
  232.     {"unset",            TclKeyword},
  233.     {"unsupported0",    TclKeyword},
  234.     {"update",            TclKeyword},
  235.     {"uplevel",            TclKeyword},
  236.     {"upvar",            TclKeyword},
  237.     {"vwait",            TclKeyword},
  238.     {"while",            TclKeyword},
  239.  
  240.     {"bell",            TkKeyword},
  241.     {"bind",            TkKeyword},
  242.     {"bindtags",        TkKeyword},
  243.     {"bitmap",            TkKeyword},
  244.     {"button",            TkKeyword},
  245.     {"canvas",            TkKeyword},
  246.     {"checkbutton",        TkKeyword},
  247.     {"clipboard",        TkKeyword},
  248.     {"destroy",            TkKeyword},
  249.     {"dialog",            TkKeyword},
  250.     {"entry",            TkKeyword},
  251.     {"focus",            TkKeyword},
  252.     {"focusNext",        TkKeyword},
  253.     {"frame",            TkKeyword},
  254.     {"grab",            TkKeyword},
  255.     {"grid",            TkKeyword},
  256.     {"image",            TkKeyword},
  257.     {"label",            TkKeyword},
  258.     {"listbox",            TkKeyword},
  259.     {"lower",            TkKeyword},
  260.     {"menu",            TkKeyword},
  261.     {"menubar",            TkKeyword},
  262.     {"menubutton",        TkKeyword},
  263.     {"message",            TkKeyword},
  264.     {"option",            TkKeyword},
  265.     {"optionMenu",        TkKeyword},
  266.     {"options",            TkKeyword},
  267.     {"pack",            TkKeyword},
  268.     {"pack-old",        TkKeyword},
  269.     {"palette",            TkKeyword},
  270.     {"photo",            TkKeyword},
  271.     {"place",            TkKeyword},
  272.     {"popup",            TkKeyword},
  273.     {"radiobutton",        TkKeyword},
  274.     {"raise",            TkKeyword},
  275.     {"scale",            TkKeyword},
  276.     {"scrollbar",        TkKeyword},
  277.     {"selection",        TkKeyword},
  278.     {"send",            TkKeyword},
  279.     {"text",            TkKeyword},
  280.     {"tk",                TkKeyword},
  281.     {"tkButtonDown",    TkKeyword},
  282.     {"tkButtonLeave",    TkKeyword},
  283.     {"tkButtonUp",        TkKeyword},
  284.     {"tkCancelRepeat",    TkKeyword},
  285.     {"tkCheckRadioInvoke",    TkKeyword},
  286.     {"tkEntryBackspace",    TkKeyword},
  287.     {"tkEntryInsert",        TkKeyword},
  288.     {"tkEntrySeeInsert",    TkKeyword},
  289.     {"tkEntrySetCursor",    TkKeyword},
  290.     {"tkEntryTranspose",    TkKeyword},
  291.     {"tkFirstMenu",            TkKeyword},
  292.     {"tkListboxBeginExtend",    TkKeyword},
  293.     {"tkListboxBeginSelect",    TkKeyword},
  294.     {"tkListboxDataExtend",        TkKeyword},
  295.     {"tkListboxUpDown",        TkKeyword},
  296.     {"tkMbMotion",            TkKeyword},
  297.     {"tkMbPost",            TkKeyword},
  298.     {"tkMenuEscape",        TkKeyword},
  299.     {"tkMenuFirstEntry",        TkKeyword},
  300.     {"tkMenuInvoke",        TkKeyword},
  301.     {"tkMenuMotion",        TkKeyword},
  302.     {"tkPostOverPoint",        TkKeyword},
  303.     {"tkSaveGrabInfo",        TkKeyword},
  304.     {"tkScaleActivate",        TkKeyword},
  305.     {"tkScaleButtonDown",        TkKeyword},
  306.     {"tkScaleControlPress",        TkKeyword},
  307.     {"tkScaleIncrement",        TkKeyword},
  308.     {"tkScrollButtonDown",        TkKeyword},
  309.     {"tkScrollButtonUp",        TkKeyword},
  310.     {"tkScrollByPages",        TkKeyword},
  311.     {"tkScrollByUnits",        TkKeyword},
  312.     {"tkTextAutoScan",        TkKeyword},
  313.     {"tkTextPaste",            TkKeyword},
  314.     {"tkTextResetAnchor",        TkKeyword},
  315.     {"tkTextScrollPages",        TkKeyword},
  316.     {"tkTextTranspose",        TkKeyword},
  317.     {"tkTraverseToMenu",        TkKeyword},
  318.     {"tk_textPaste",        TkKeyword},
  319.     {"tkerror",            TkKeyword},
  320.     {"tkvars",            TkKeyword},
  321.     {"tkwait",            TkKeyword},
  322.     {"toplevel",            TkKeyword},
  323.     {"winfo",            TkKeyword},
  324.     {"wm",                TkKeyword}
  325. };
  326.  
  327.  
  328.  
  329. //
  330. //  Private routines (forward referenced)...
  331. //
  332. //     The scan_... routines will typically set the lpPC->ParseState member
  333. //     based on what they find...
  334. //
  335. static int isodigit(char digit);
  336. static int scan_number(char* next_token, parse_context_type *lpPC);
  337.  
  338. static int tokenCompare(const void* n1, const void* n2);
  339. static parse_token_type idToken(TclScanGlobals* g, char* token);
  340. static void sortTokens(TclScanGlobals* g);
  341. static void addToken(TclScanGlobals* g, char* token, parse_token_type type);
  342.  
  343.  
  344. ////////////////////////////////////////////////////////////////////////
  345. //
  346. //  API Definitions...
  347. //
  348.  
  349.  
  350. // char *ParseEngineName()
  351. //  
  352. //     Purpose:
  353. //        Tell Lemmy the name to use internally for this engine.  Up to 32 
  354. //        characters.  If two DLLs emit the same name, they will be in conflict
  355. //        and the first one that appears in a directory scan will be used.
  356. // 
  357. //     Returns:
  358. //        Null-terminated string with the parse engine name.
  359. // 
  360. DllExport(char *) ParseEngineName()
  361. {
  362.    static char name[32];
  363.  
  364.    strcpy( name, "Tcl/Tk Parser" );
  365.    return(name);
  366. }
  367.  
  368. // short ParsePropertySheetAvailable()
  369. // 
  370. //     Purpose:
  371. //        Tell whether or not this DLL provides a property sheet dialog.
  372. // 
  373. //     Returns:
  374. //        0 - The "ParsePropertySheet" function is not supported.
  375. //        1 - The "ParsePropertySheet" function will display a dialog. 
  376. // 
  377. DllExport(short) ParsePropertiesAvailable()                     
  378.     return(0); 
  379. }
  380.  
  381. // short ParsePropertySheet(HWND hWndParent)
  382. // 
  383. //     Purpose:
  384. //        Implement this function to display a property sheet for your
  385. //        parse engine if so desired.  The parent window to reference in
  386. //        your dialog box is passed as a parameter.
  387. // 
  388. //     Returns:
  389. //        0 = Successful, -1 = Unsuccessful.
  390. // 
  391. DllExport(short) ParseProperties(HWND hWndParent)               
  392. {
  393.     return(-1);
  394. }
  395.  
  396. // short ParseCustomKeywordsSupported()
  397. //    
  398. //     Purpose:
  399. //        Tell Lemmy whether or not custom keyword lists are supported by
  400. //        your engine.
  401. // 
  402. //     Returns:
  403. //        1 = TRUE  - Lemmy will allocate and initialize the CustomKeyword info
  404. //                    if a keyword list is defined.
  405. //        0 = FALSE - Lemmy will inform the user that custom keyword lists are
  406. //                    not supported.
  407. // 
  408. // 
  409. DllExport(short) ParseCustomKeywordsSupported()                    
  410.     return(1); 
  411. }
  412.  
  413.  
  414. // short ParseLoadCustomTokens( parse_context_type *lpPC )
  415. // 
  416. //     Purpose:
  417. //        This entry point will be called when Custom token definitions need
  418. //        to be re-initialized from their saved values.  The LoadCustomToken
  419. //        call is provided as a default way to load custom token attributes in
  420. //        the system registry.
  421. // 
  422. //     Returns:
  423. //        0 = Successful, -1 = Unsuccessful.
  424. // 
  425. //        
  426. DllExport(short) ParseLoadCustomTokens( parse_context_type *lpPC ) 
  427.     int i;
  428.  
  429.     for(i=0; i < lpPC->CustomTokenMax; i++) 
  430.         LoadTokenInfo( &(lpPC->CustomToken[i]) );
  431.  
  432.     return(0); 
  433. }
  434.  
  435. // short ParseSaveCustomTokens( parse_context_type *lpPC )
  436. // 
  437. //     Purpose:
  438. //        If Lemmy edits the Custom Token definitions for your parse engine,
  439. //        and the user wants to save the new definitions, this routine will
  440. //        be called.  An implementation of this routine should save the CustomToken
  441. //        data in a way that it can be reloaded when ParseConstructor is called
  442. //        on a future run.  The SaveCustomToken call is provided as a default
  443. //        way to save custom token attributes in the system registry.
  444. // 
  445. //     Returns:
  446. //        0 = Successful, -1 = Unsuccessful.
  447. // 
  448. //        
  449. DllExport(short) ParseSaveCustomTokens( parse_context_type *lpPC ) 
  450.     int i;
  451.  
  452.     for(i=0; i < lpPC->CustomTokenMax; i++) 
  453.         SaveTokenInfo( &(lpPC->CustomToken[i]) );
  454.  
  455.     return(0); 
  456. }
  457.  
  458.  
  459.  
  460. // Return a malloc'd copy of a string
  461. //
  462. static char* copyOf(char* s)
  463. {
  464.     char* t;
  465.  
  466.     t = malloc(strlen(s)+1);
  467.     if(t == NULL) return(NULL);
  468.  
  469.     strcpy(t,s);
  470.     return(t);
  471. }
  472.  
  473.  
  474. //    Purpose:
  475. //       This is called during initialization of the parse engine(s) by Lemmy.
  476. //      Initialize variables and allocate storage here.
  477. //
  478. //    Returns:
  479. //       0 = Success, -1 = Failure.
  480. //
  481. //       lpPC->lpExtra
  482. //          Optional storage used by your engine can be allocated, and a
  483. //          reference to it placed in this "void" pointer field.
  484. //
  485. DllExport(short) ParseConstructor( parse_context_type *lpPC )
  486. {
  487.     int i, n, nCustom;
  488.     TclScanGlobals* g;
  489.  
  490.  
  491.     // Handle the custom token types for Tcl
  492.     //
  493.     nCustom = sizeof(CustomTokenTypes)/sizeof(char*);
  494.  
  495.     lpPC->CustomToken = (custom_token_type*) malloc(sizeof(custom_token_type) * nCustom);
  496.     if(lpPC->CustomToken == NULL) return(-1);
  497.  
  498.     lpPC->CustomTokenMax = nCustom;
  499.     for(i=0; i < nCustom; i++) 
  500.         if((lpPC->CustomToken[i].label = copyOf(CustomTokenTypes[i])) == NULL) return(-1);
  501.  
  502.     ParseLoadCustomTokens(lpPC);
  503.  
  504.     // Allocate and initialize the TclScanGlobals
  505.     //
  506.     lpPC->lpExtra =  malloc(sizeof(TclScanGlobals));
  507.     if(lpPC->lpExtra == NULL) return(-1);
  508.  
  509.     g = (TclScanGlobals*) lpPC->lpExtra;
  510.  
  511.     n = sizeof(InitTokenTable)/sizeof(Token);
  512.  
  513.     for(i=0; i < n; i++) {
  514.         g->tokenTable[i].token = InitTokenTable[i].token;
  515.         g->tokenTable[i].type = InitTokenTable[i].type;
  516.     }
  517.  
  518.     g->knownTokens = n;
  519.  
  520.     // Add the custom keywords
  521.     //
  522.     if(lpPC->CustomKeywordList) {
  523.         for(i=0; i < lpPC->CustomKeywordMax; i++) 
  524.             addToken(g,lpPC->CustomKeywordList[i].value,CustomKeyword);
  525.     }
  526.  
  527.     // Sort the keyword table to make it bsearch()able
  528.     //
  529.     sortTokens(g);
  530.  
  531.     g->flag_period = FALSE;
  532.     g->flag_exp    = FALSE;
  533.  
  534.     return(0);
  535. }
  536.  
  537.  
  538.  
  539. // short ParseDestructor( parse_context_type *lpPC )
  540. // 
  541. //     Purpose:
  542. //        Called when parse engine objects are being destroyed.  Any memory
  543. //        allocated in ParseConstructor should be freed here.
  544. // 
  545. //     Returns:
  546. //        0 = Success, -1 = Failure.
  547. //    
  548. //        lpPC->lpExtra
  549. //           If memory referenced here was allocated in the constructor, free
  550. //           it here.  Lemmy is not responsible for the content of this pointer.
  551. //
  552. DllExport(short) ParseDestructor( parse_context_type *lpPC )
  553. {
  554.     int i;
  555.  
  556.     for(i=0; i < lpPC->CustomTokenMax; i++) 
  557.         free(lpPC->CustomToken[i].label);
  558.  
  559.     free(lpPC->CustomToken);
  560.     free(lpPC->lpExtra);
  561.     return(0);
  562. }
  563.  
  564.  
  565. // short ParseInitRedraw( parse_context_type *lpPC )
  566. // 
  567. //     Purpose:
  568. //        Called at the beginning of a file redraw.  Make any initializations
  569. //        here on your optional storage.
  570. // 
  571. //     Returns:
  572. //        0 = Success, -1 = Failure.
  573. //
  574. DllExport(short) ParseInitRedraw( parse_context_type *lpPC )
  575. {
  576.    return(0);
  577. }
  578.  
  579.  
  580.  
  581. // short ParseInitParser( parse_context_type *lpPC )
  582. // 
  583. //     Purpose:
  584. //        Called when the parse state should be reset (can be called more than
  585. //        once during a redraw).  Note:  It is not necessary to call this routine
  586. //        from ParseInitRedraw - it will be called as well automatically.
  587. // 
  588. //     Returns:
  589. //        0 = Success, -1 = Failure.
  590. //
  591. DllExport(short) ParseInitParser( parse_context_type *lpPC )
  592. {
  593.    return(0);
  594. }
  595.  
  596.  
  597.  
  598. // short ParseBackscan( parse_context_type *lpPC )
  599. // 
  600. //     Purpose:
  601. //        Called during "backscan" initialization.  Lemmy steps backward through
  602. //        the file looking for a pattern on a line that indicates a good place
  603. //        to start parsing from (i.e.: the beginning of a multi-line comment
  604. //        block).  There is a settable "backscan" limit in Lemmy that will limit
  605. //        the number of times this routine is called during initialization.
  606. // 
  607. //     Parameters:
  608. //        lpPC->lpParseText
  609. //           The text to be scanned.  Do not update the pointer, or change the
  610. //           text.
  611. //  
  612. //     Returns:
  613. //        0 = Success, -1 = Failure. (there should rarely be reason for this 
  614. //           call to fail).
  615. // 
  616. //        lpPC->FinishBackscan
  617. //           Set to 1 (true) when you have found a pattern that you want to stop
  618. //           "backscanning" on.
  619. // 
  620. DllExport(short) ParseBackscan( parse_context_type *lpPC )
  621. {
  622.    char *text;
  623.  
  624.    text = lpPC->lpParseText;
  625.  
  626.    if ( (strlen( text ) >= 2) &&
  627.         ( text[strlen(text)-1] == '\\') )
  628.    {
  629.       return(0);
  630.    }
  631.  
  632.    lpPC->FinishBackscan = TRUE;
  633.    return(0);
  634. }
  635.  
  636.  
  637.  
  638.  
  639.  
  640.  
  641. // short ParseScanPreprocessor( parse_context_type *lpPC )
  642. // 
  643. //     Purpose:
  644. //        Test the line to see if its a preprocessor line.   Check the parse
  645. //        state in the event that this is a continuation of a multi-line
  646. //        pre-processor statement.
  647. // 
  648. //     Parameters:
  649. //        lpPC->lpParseText (Input/Output)
  650. //           The text to be scanned.  Update this pointer - past the scanned
  651. //           text on return (i.e. pointing at the next token or pointing at
  652. //           the string's null terminator).  
  653. // 
  654. //           *** In this routine, the pointer should only be moved if the 
  655. //           routine determines that this is a preprocessor line.
  656. // 
  657. //        lpPC->ParseState (Input/Output)
  658. //           The current parse state.  This is either a fresh start, or a
  659. //           continuation of some sort.  If the value is "st_continue_previous",
  660. //           then look at lpPC->ScannedToken to find out what token we are
  661. //           in the middle of.
  662. // 
  663. //           On return, set this (typically) to either st_fresh_start if the
  664. //           end of the token was found, or st_continue_previous if the token
  665. //           continues to the next line.
  666. // 
  667. //        lpPC->ScannedToken (Input/Output)
  668. //           Set to the token that was detected.  For this particular routine
  669. //           the valid values would either be "tk_none" or "tk_preprocessor".
  670. //           Any other construct is detected in the ParseToken call.
  671. // 
  672. //    TclScan: Not relevant
  673. // 
  674. DllExport(short) ParseScanPreprocessor( parse_context_type *lpPC )
  675. {
  676.     return(0);
  677. }
  678.  
  679.  
  680.  
  681. // short ParseToken( parse_context_type *lpPC )
  682. // 
  683. //     Purpose:
  684. //        Finally - this is the workhorse.  Scan anything except preprocessor
  685. //        text.
  686. // 
  687. //     Parameters:
  688. //        lpPC->lpParseText (Input/Output)
  689. //           The text to be scanned.  Update this pointer - past the scanned
  690. //           text on return (i.e. pointing at the next token or pointing at
  691. //           the string's null terminator).  
  692. // 
  693. //        lpPC->ParseState (Input/Output)
  694. //           The current parse state.  This is either a fresh start, or a
  695. //           continuation of some sort.  If the value is "st_continue_previous",
  696. //           then look at lpPC->ScannedToken to find out what token we are
  697. //           in the middle of.
  698. // 
  699. //           On return, set this (typically) to either st_fresh_start if the
  700. //           end of the token was found, or st_continue_previous if the token
  701. //           continues to the next line.
  702. // 
  703. //        lpPC->ScannedToken (Input/Output)
  704. //           On entry, this field indicates the token being scanned from the
  705. //           previous line if lpPC->ParseState is not set to "st_fresh_start.
  706. // 
  707. //           On return, set to the token that was detected.  
  708. // 
  709. DllExport(short) ParseToken( parse_context_type *lpPC )
  710. {
  711.     char  *next_token;
  712.     char  *tmp_pointer;
  713.     char      akeyword[MAX_KEYWORD_LEN];        // 
  714.     TclScanGlobals* g = (TclScanGlobals*) lpPC->lpExtra;
  715.  
  716.     g->flag_period = FALSE; 
  717.     g->flag_exp    = FALSE;
  718.  
  719.     next_token = SkipWhiteSpace( lpPC->lpParseText );
  720.  
  721.     if(lpPC->ScanColumn == 0) {    // beginning of line
  722.         if(*next_token == '#') {        // comment line
  723.             lpPC->lpParseText = &lpPC->lpParseText[strlen(lpPC->lpParseText)]; // null terminator
  724.             lpPC->ParseState = st_fresh_start;
  725.             lpPC->ScannedToken = Comment;
  726.  
  727.             return(0);
  728.         }
  729.     }
  730.  
  731.     //
  732.     //  '.' could be either struct or float - advance over the period if float
  733.     //
  734.     if(*next_token == '.' && isdigit(*(next_token+1))) {
  735.         g->flag_period = TRUE;
  736.         next_token++;
  737.     }
  738.  
  739.     //
  740.     //  '_' is really considered alpha
  741.     //  If punctuation of some type...figure it out
  742.     //
  743.     if(ispunct( *next_token ) && *next_token != '_') {
  744.         if(*next_token == '[' || *next_token == ']') {
  745.             next_token++;
  746.  
  747.             lpPC->lpParseText = SkipWhiteSpace(next_token);
  748.             lpPC->ScannedToken = Brackets;
  749.             return(0);
  750.         } 
  751.         else if(*next_token == '{' || *next_token == '}') {
  752.             next_token++;
  753.  
  754.             lpPC->lpParseText = SkipWhiteSpace(next_token);
  755.             lpPC->ScannedToken = Braces;
  756.             return(0);
  757.         }
  758.         // If next token is of COMMENT (;# variety) type
  759.         else if ( *next_token == ';' && *(next_token + 1) == '#' ) {
  760.             lpPC->lpParseText = &lpPC->lpParseText[strlen(lpPC->lpParseText)]; // null terminator
  761.             lpPC->ParseState = st_fresh_start;
  762.             lpPC->ScannedToken = Comment;
  763.  
  764.             return(0);
  765.         }
  766.  
  767.         //
  768.         //  Default...
  769.         //
  770.         else {
  771.             ++(lpPC->lpParseText);
  772.             lpPC->ParseState   = st_fresh_start;
  773.             lpPC->ScannedToken = Unknown;
  774.             return(0);
  775.         }
  776.     }
  777.  
  778.     // Number of some type ... decimal, hex, or octal
  779.     // If scan_number() doesn't recognize it, we still attempt to
  780.     // match it as a keyword.
  781.     //
  782.     else if(isdigit(*next_token) && scan_number(next_token, lpPC) == 0) {
  783.         return(0);
  784.     }
  785.       
  786.     // Keywords...
  787.     else {
  788.         int tokenLen = 0;
  789.         tmp_pointer = next_token;
  790.  
  791.         //
  792.         //  Suck it in...
  793.         //
  794.         while (( isalnum( *tmp_pointer ) || *tmp_pointer == '_' ) && *tmp_pointer ) {
  795.             tmp_pointer++;
  796.         }
  797.  
  798.         tokenLen = tmp_pointer - next_token;
  799.         lpPC->lpParseText = SkipWhiteSpace(tmp_pointer);
  800.  
  801.         lpPC->ParseState = st_fresh_start;
  802.         if(tokenLen >= MAX_KEYWORD_LEN) {
  803.             lpPC->ScannedToken = Unknown;
  804.         }
  805.         else {
  806.             strncpy( akeyword, next_token, (tmp_pointer - next_token) );
  807.             akeyword[ tmp_pointer - next_token ] = '\0';
  808.  
  809.             lpPC->ScannedToken = idToken((TclScanGlobals*)lpPC->lpExtra,akeyword);
  810.         }
  811.  
  812.         return(0);
  813.     }
  814. }
  815.  
  816.  
  817.  
  818. ////////////////////////////////////////////////////////////////////////
  819. //
  820. //  Internal Routines...
  821. //
  822. ////////////////////////////////////////////////////////////////////////
  823.  
  824. // Returns TRUE if digit is an octal digit
  825. //
  826. static int isodigit( char digit )
  827. {
  828.     if ( digit >= '0' && digit <= '7' )
  829.         return( TRUE );
  830.  
  831.     return( FALSE );
  832. }
  833.  
  834.  
  835. // Called when the next character is a digit, attempts to consume
  836. // a number according to C's styles.  Code to handle trailing LlUu's etc.
  837. // has been removed, see the original cppscan if you need to recognize
  838. // that kind of number.
  839. //
  840. // If we return 0 (success), ParseToken() should immediately return 0.
  841. // However, if we don't get a perfectly-formatted number here, we don't
  842. // assume that it's Unknown.  If we return -1, ParseToken() should attempt
  843. // to examine this token further.
  844. //
  845. static int scan_number(char* next_token, parse_context_type *lpPC)
  846. {
  847.     TclScanGlobals* g = (TclScanGlobals*) lpPC->lpExtra;
  848.  
  849.     // Hex
  850.     if ( *next_token == '0' && *(next_token + 1) =='x' && !g->flag_period ) {
  851.         next_token += 2;
  852.  
  853.         // To catch errors like '0x'... incomplete hex
  854.         if ( !isxdigit( *next_token )) 
  855.             return(-1);
  856.  
  857.         while ( isxdigit( *next_token ) && *next_token ) 
  858.             next_token++;
  859.  
  860.         if ( isalpha( *next_token ) ) 
  861.             return(-1);
  862.  
  863.         lpPC->lpParseText    = SkipWhiteSpace(next_token);
  864.         lpPC->ParseState   = st_fresh_start;
  865.         lpPC->ScannedToken = Number;
  866.     }
  867.  
  868.     // Octal..
  869.     else if ( *next_token == '0' && isdigit( *(next_token + 1) ) && !g->flag_period ) {
  870.         next_token += 1;
  871.  
  872.         while ( isodigit( *next_token ) && *next_token ) {
  873.             next_token++;
  874.         }
  875.  
  876.         // Trap is here */
  877.         if ( isalnum( *next_token ) ) 
  878.             return(-1);
  879.  
  880.  
  881.         lpPC->lpParseText = SkipWhiteSpace(next_token);
  882.         lpPC->ParseState = st_fresh_start;
  883.         lpPC->ScannedToken = Number;
  884.         return(0);
  885.     }  // end of octal
  886.  
  887.     /* Must be decimal */
  888.     else {
  889. Decimal:
  890.         while ( isdigit( *next_token ) && *next_token ) 
  891.             next_token++;
  892.  
  893.         // Handle decimal place in a number...insure only one
  894.         if ( *next_token == '.' ) {
  895.             next_token++;
  896.  
  897.             /* Period already set...? strange number :-) */
  898.             if ( g->flag_period ) 
  899.                 return(-1);
  900.             else {
  901.                 g->flag_period = TRUE;
  902.                 goto Decimal;
  903.             }
  904.         }
  905.  
  906.         // Handle [Ee] for floats
  907.         if ( tolower( *next_token ) == 'e' ) {
  908.             next_token++;
  909.  
  910.             // Second [Ee]?
  911.             if ( g->flag_exp ) 
  912.                 return(-1);
  913.             else {
  914.                 g->flag_exp = TRUE;
  915.  
  916.                 // Absorb sign if present
  917.                 if ( *next_token == '+' || *next_token == '-' ) {
  918.                     next_token++;
  919.                 }
  920.  
  921.                 // Make sure next value is a number for exponent
  922.             if ( !isdigit( *next_token ) ) 
  923.                 return(-1);
  924.  
  925.             // Continue parsing otherwise 
  926.             goto Decimal;
  927.             }
  928.         }
  929.  
  930.         if ( isalpha( *next_token ) ) 
  931.             return(-1);
  932.  
  933.         lpPC->lpParseText = SkipWhiteSpace(next_token);
  934.         lpPC->ParseState = st_fresh_start;
  935.         lpPC->ScannedToken = Number;
  936.         return(0);
  937.     }  // end of decimal
  938.  
  939.     return(0);        // just in case it's missed above
  940. }
  941.  
  942. // qsort() and bsearch() comparison function for Tokens
  943. //
  944. static int tokenCompare(const void* n1, const void* n2)
  945. {
  946.     return(strcmp(((const Token*)n1)->token, ((const Token*)n2)->token));
  947. }
  948.  
  949. // Return the parse type for the given token.  If not recognized,
  950. // tk_none is returned.
  951. //
  952. parse_token_type idToken(TclScanGlobals* g,char* token)
  953. {
  954.     Token*    t;
  955.     Token    key;
  956.  
  957.     key.token = token;
  958.     t = (Token*) bsearch(&key,g->tokenTable,
  959.         g->knownTokens,sizeof(Token),tokenCompare);
  960.  
  961.     if(t == NULL)
  962.         return(Unknown);
  963.     else 
  964.         return(t->type);
  965. }
  966.  
  967. // Sort the token table
  968. //
  969. static void sortTokens(TclScanGlobals* g)
  970. {
  971.     qsort(g->tokenTable,g->knownTokens, sizeof(Token), tokenCompare);
  972. }
  973.  
  974. // Add a new token to the table.  Call sortTokens() after 1 or 
  975. // more invocations of this function.
  976. //
  977. // If we have no more room, quietly refuse to add the token.
  978. //
  979. static void addToken(TclScanGlobals* g, char* token, parse_token_type type)
  980. {
  981.     if(g->knownTokens >= MAX_KNOWN_TOKENS) return;
  982.  
  983.     g->tokenTable[g->knownTokens].token = token;
  984.     g->tokenTable[g->knownTokens].type = type;
  985.  
  986.     g->knownTokens++;
  987. }
  988.